package com.itheima.controller;
import com.itheima.mapper.PrejectMapper;
import com.itheima.pojo.FileDto;
import com.itheima.utils.Wangeditor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.itheima.utils.Result;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@RequestMapping("/upload")
@Validated
@Slf4j
public class UploadController {

    @Autowired
    private PrejectMapper prejectMapper;
    public static final String UPLOAD_PATH = "D:\\media\\roomhotel\\src\\main\\resources\\static\\img";

    //单文件或者小文件上传
    @PostMapping("/singleFileUpload")
    public Result creates(@RequestBody MultipartFile file,String prefix) throws IOException {
        String ext = StringUtils.getFilename(file.getOriginalFilename());
        if (prefix == null){
            //生成随机UUID作为文件名
            prefix = String.valueOf(UUID.randomUUID());
        }
        File dstFile = new File(UPLOAD_PATH, String.format("%s.%s",prefix, ext));
        file.transferTo(dstFile);
        return Result.success("http://localhost:8082/static/img/"+prefix+"."+ext);
    }
    //富文本编辑器上传图片 返回格式不一样
    @PostMapping("/wangeditorUpload")
    public Wangeditor wangeditorUpload(@RequestBody MultipartFile file,String prefix) throws IOException {
        String ext = StringUtils.getFilename(file.getOriginalFilename());
        if (prefix == null){
            //生成随机UUID作为文件名
            prefix = String.valueOf(UUID.randomUUID());
        }
        File dstFile = new File(UPLOAD_PATH, String.format("%s.%s",prefix, ext));
        file.transferTo(dstFile);
        Map<String,String> data = new HashMap<>();
        data.put("url", "http://localhost:8082/static/img/"+prefix+"."+ext);
        data.put("ait", prefix+"."+ext);
        data.put("href", "http://localhost:8082/static/img/"+prefix+"."+ext);
        return Wangeditor.success(data);
    }
    //多文件或者小文件上传
    @PostMapping("/multiFileUploads")
    public Result create(@RequestBody MultipartFile[] file) throws IOException {
        ArrayList<String> pathList = new ArrayList<>();
        for (MultipartFile f : file) {
            if (!f.isEmpty()) {
                String billId = String.valueOf(UUID.randomUUID());
                String ext = StringUtils.getFilename(f.getOriginalFilename());
                File dstFile = new File(UPLOAD_PATH, String.format("%s.%s", billId, ext));
                f.transferTo(dstFile);
                System.out.println(f.getOriginalFilename());
                pathList.add(billId + "." + ext);
            }
        }
        System.out.println(pathList);
        return Result.success(pathList);
    }

    // 大文件上传
    //检测之前分片是否上传
    @PostMapping("/checkblock")
    public Result checkMd5( @RequestBody HttpServletRequest request, HttpServletResponse response){
        //当前分片
        String chunk = request.getParameter("chunk");
        //分片大小
        String chunkSize = request.getParameter("chunkSize");
        //当前文件的MD5值
        String guid = request.getParameter("guid");
        //分片上传路径
        System.out.println(chunkSize+chunk+guid);
        String tempPath = UPLOAD_PATH+File.separator + "temp";
        File checkFile = new File(tempPath + File.separator + guid + File.separator + chunk);
        response.setContentType("text/html;charset=utf-8");
        //如果分片存在，并且长度等于上传大小
        try {
            if (checkFile.exists() && checkFile.length()==Integer.parseInt(chunkSize)){
                response.getWriter().write("{\"ifExist\"}:1");
            } else {
                response.getWriter().write("{\"ifExist\"}:0");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return Result.success(chunk);
    }

    //分片上传
    /**
     * 上传分片
     *
     * @param file
     * @param chunk
     * @param guid
     * @throws IOException
     */
    @PostMapping("save")
    @ResponseBody
    public Result upload(@RequestParam MultipartFile file, Integer chunk, String guid) throws IOException {
        String filePath = UPLOAD_PATH + File.separator + "temp" + File.separator + guid;
        File tempfile = new File(filePath);
        if (!tempfile.exists()) {
            tempfile.mkdirs();
        }
        RandomAccessFile raFile = null;
        BufferedInputStream inputStream = null;
        if (chunk == null) {
            chunk = 0;
        }
        try {
            File dirFile = new File(filePath, String.valueOf(chunk));
            //以读写的方式打开目标文件
            raFile = new RandomAccessFile(dirFile, "rw");
            raFile.seek(raFile.length());
            inputStream = new BufferedInputStream(file.getInputStream());
            byte[] buf = new byte[1024];
            int length = 0;
            while ((length = inputStream.read(buf)) != -1) {
                raFile.write(buf, 0, length);
            }
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (raFile != null) {
                raFile.close();
            }
        }
        return Result.success();
    }

    //合并上传
    /**
     * 合并文件
     *
     * @param fileDto
     */
    @PostMapping("combine")
    @ResponseBody
    public Result combineBlock(@RequestBody FileDto fileDto) {
        String guid = fileDto.getGuid();
        String fileName = fileDto.getFileName();
        //分片文件临时目录
        File tempPath = new File(UPLOAD_PATH + File.separator + "temp" + File.separator + guid);
        //真实上传路径
        File realPath = new File(UPLOAD_PATH + File.separator + "real");
        if (!realPath.exists()) {
            realPath.mkdirs();
        }
        File realFile = new File(UPLOAD_PATH + File.separator + "real" + File.separator + fileName);
        FileOutputStream os = null;// 文件追加写入
        FileChannel fcin = null;
        FileChannel fcout = null;
        try {
            log.info("合并文件——开始 [ 文件名称：" + fileName + " ，MD5值：" + guid + " ]");
            os = new FileOutputStream(realFile, true);
            fcout = os.getChannel();
            if (tempPath.exists()) {
                //获取临时目录下的所有文件
                File[] tempFiles = tempPath.listFiles();
                //按名称排序
                Arrays.sort(tempFiles, (o1, o2) -> {
                    if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
                        return -1;
                    }
                    if (Integer.parseInt(o1.getName()) == Integer.parseInt(o2.getName())) {
                        return 0;
                    }
                    return 1;
                });
                //每次读取10MB大小，字节读取
                //byte[] byt = new byte[10 * 1024 * 1024];
                //int len;
                //设置缓冲区为10MB
                ByteBuffer buffer = ByteBuffer.allocate(5 * 1024 * 1024);
                for (int i = 0; i < tempFiles.length; i++) {
                    FileInputStream fis = new FileInputStream(tempFiles[i]);
                    /*while ((len = fis.read(byt)) != -1) {
                        os.write(byt, 0, len);
                    }*/
                    fcin = fis.getChannel();
                    if (fcin.read(buffer) != -1) {
                        buffer.flip();
                        while (buffer.hasRemaining()) {
                            fcout.write(buffer);
                        }
                    }
                    buffer.clear();
                    fis.close();
                    //删除分片
                    tempFiles[i].delete();
                }
                os.close();
                //删除临时目录
                if (tempPath.isDirectory() && tempPath.exists()) {
                    System.gc(); // 回收资源
                    tempPath.delete();
                }
                log.info("文件合并——结束 [ 文件名称：" + fileName + " ，MD5值：" + guid + " ]");
            }
        } catch (Exception e) {
            log.error("文件合并——失败 " + e.getMessage());
        }

        return Result.success(fileName);
    }

    /**
     * 查询上传目录下的全部文件
     *
     * @return
     */
    @GetMapping("/getFiles")
    @ResponseBody
    public Result getFiles(String fileName) {
        String realUploadPath = UPLOAD_PATH + File.separator + "real";
        File zipFile = new File(realUploadPath + File.separator + fileName);
        if (zipFile.exists()){
            return Result.success();
        }
        else {
            return Result.error("文件不存在");
        }
    }
    /**
     * Long 转 Date
     *
     * @param time
     * @return
     */
    private String getDate(Long time) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        Date date = new Date(time);
        return sdf.format(date);
    }


    /**
     * 文件下载
     *
     * @param fileName 文件名称
     * @param response HttpServletResponse
     */
    @GetMapping("downloadFile")
    @ResponseBody
    public void downLoadFile(String fileName, HttpServletResponse response) {
        if (fileName.equals("")){
            return;
        }
        File file = new File(UPLOAD_PATH + File.separator + "real" + File.separator + fileName);
        if (file.exists()) {
            InputStream is = null;
            OutputStream os = null;
            try {
                response.reset();
                // 设置强制下载不打开
                response.setContentType("application/force-download");
                //设置下载文件名
                response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
                response.addHeader("Content-Length", "" + file.length());
                //定义输入输出流
                os = new BufferedOutputStream(response.getOutputStream());
                is = new BufferedInputStream(new FileInputStream(file));
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) > 0) {
                    os.write(buffer, 0, len);
                    os.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                log.info("文件下载成功——文件名：" + fileName);
            }
        }
    }

    /**
     * 文件下载
     *
     * @param fileName 文件名称
     * @param response HttpServletResponse
     */
    @GetMapping("downloadExcel")
    @ResponseBody
    public void downLoadExcel(String fileName, HttpServletResponse response) {
        if (fileName.equals("")){
            return;
        }
        File file = new File(UPLOAD_PATH + File.separator + "real" + File.separator + fileName);
        if (file.exists()) {
            InputStream is = null;
            OutputStream os = null;
            try {
                response.reset();
                // 设置强制下载不打开
                response.setContentType("application/force-download");
                //设置下载文件名
                response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
                response.addHeader("Content-Length", "" + file.length());
                //定义输入输出流
                os = new BufferedOutputStream(response.getOutputStream());
                is = new BufferedInputStream(new FileInputStream(file));
                byte[] buffer = new byte[1024];
                int len;
                while ((len = is.read(buffer)) > 0) {
                    os.write(buffer, 0, len);
                    os.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                log.info("文件下载成功——文件名：" + fileName);
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileName
     * @return
     */
    @GetMapping("/delFile")
    @ResponseBody
    public Result delFile(String fileName) {
        boolean b = false;
        File file = new File(UPLOAD_PATH + File.separator + "real" + File.separator + fileName);
        if (file.exists() && file.isFile()) {
            b = file.delete();
        }
        if (b){
            return Result.success();
        }
        return Result.error("删除失败");
    }
    /**
     * 删除图片
     *
     * @param fileName
     * @return
     */
    @GetMapping("/delImgFile")
    @ResponseBody
    public Result delImgFile(String fileName) {
        boolean b = false;
        File file = new File(UPLOAD_PATH + File.separator + fileName);
        if (file.exists() && file.isFile()) {
            b = file.delete();
        }
        if (b){
            return Result.success();
        }
        return Result.error("删除失败");
    }
    @GetMapping("/getFileContent")
    @ResponseBody
    public ResponseEntity<byte[]> getFileContent(@RequestParam("fileName") String fileName) {
        File file = new File(UPLOAD_PATH + File.separator + "real" + File.separator + fileName);
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }
        try (InputStream inputStream = new FileInputStream(file)) {
            byte[] fileContent = inputStream.readAllBytes();
            // 使用RFC 5987编码文件名
            String encodedFilename = "UTF-8''" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFilename + "\"")
                    .contentType(MediaType.MULTIPART_FORM_DATA)
                    .body(fileContent);
        } catch (IOException e) {
            log.error("读取文件内容时出错: " + e.getMessage());
            return ResponseEntity.status(500).build();
        }
    }














}